package com.cjj.springsecurity.config;

import com.cjj.springsecurity.security.CustomerFilterInvocationSecurityMetadataSource;
import com.cjj.springsecurity.filter.JwtAuthenticationTokenFilter;
import com.cjj.springsecurity.manager.CustomerAccessDecisionManager;
import com.cjj.springsecurity.security.JwtAuthenticationEntryPoint;
import com.cjj.springsecurity.service.UserServiceDetails;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author by BrownC_
 * @date 2021/12/21 14:52:28
 * @email ccc-ju@outlook.com
 */
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserServiceDetails userService;

    private final JwtAuthenticationEntryPoint unauthorizedHandler;

    /**
     * 密码加密方式，必须指定一种
     * 本例使用官方推荐的BCrypt强哈希函数，密钥迭代次数为2^strength，strength取值在4~31之间，默认为10
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder(15);
    }

    /**
     * 认证设置（配置用户信息：登录名、登录密码、所属角色）
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    @Bean
    public FilterRegistrationBean<JwtAuthenticationTokenFilter> registrationBean(JwtAuthenticationTokenFilter filter) {
        FilterRegistrationBean<JwtAuthenticationTokenFilter> registrationBean = new FilterRegistrationBean<>(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilterBean(){
        return new JwtAuthenticationTokenFilter();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 认证设置(HttpSecurity认证, 用户请求URL认证)
     * @param http http认证
     * @throws Exception 异常信息
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 因为SpringSecurity使用X-Frame-Options防止网页被Frame。所以需要关闭为了让后端的接口管理的swagger页面正常显示
        http.headers().frameOptions().disable();
        // 新加入,允许跨域
        http.cors()
                .and()
                // 由于使用的是JWT，我们这里不需要csrf
                .csrf().disable()
                // authorizeRequests()方法标识开启httpSecurity认证
                .authorizeRequests()
                // 对于获取token的RestApi要允许匿名访问
                //.antMatchers("/api/auth/login").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                // 异常的处理器，将执行未鉴权的处理方法
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
                .and()
                .authorizeRequests()
                // Http配置， 可以通过URL获取对应的角色信息
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(new CustomerFilterInvocationSecurityMetadataSource());
                        object.setAccessDecisionManager(new CustomerAccessDecisionManager());
                        return object;
                    }
                })
                // JwtAuthenticationTokenFilter: JWT认证过滤器,验证token有效性
                // UsernamePasswordAuthenticationFilter: 认证操作全靠这个过滤器
                .and().addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 资源放行
     * @param web 配置web静态资源
     */
    @Override
    public void configure(WebSecurity web){
        web.ignoring().antMatchers("/doc.html/**"
                , "/favicon.ico"
                , "/webjars/**"
                , "/swagger-ui.html"
                , "/swagger-ui/*"
                , "/swagger-resources/**"
                , "/v2/api-docs"
                , "/error"
                , "/api/auth/login"
        );
    }
}
